/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.modules.java;
import java.io.*;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.text.MessageFormat;
import java.util.*;
import javax.swing.*;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.border.*;
import org.openide.*;
import org.openide.cookies.ConnectionCookie;
import org.openide.nodes.Node;
import org.openide.src.*;
import org.openide.util.HelpCtx;
import org.netbeans.modules.java.settings.JavaSynchronizationSettings;
/** This class and its inner classes maintain the basic functionality and support
* for the synchronization between java sources.
*
* @author Petr Hamernik
*/
public class JavaConnections {
/** Constants used for definitions of the TYPE_XXX constants */
static final int ADD = 0x0001;
static final int REMOVE = 0x0002;
static final int CHANGE = 0x0004;
public static final int TYPE_FIELDS_ADD = ADD;
public static final int TYPE_FIELDS_REMOVE = REMOVE;
public static final int TYPE_FIELDS_CHANGE = CHANGE;
public static final int TYPE_FIELDS = (ADD | REMOVE | CHANGE);
public static final int TYPE_METHODS_ADD = ADD << 4;
public static final int TYPE_METHODS_REMOVE = REMOVE << 4;
public static final int TYPE_METHODS_CHANGE = CHANGE << 4;
public static final int TYPE_METHODS = (ADD | REMOVE | CHANGE) << 4;
public static final int TYPE_CLASSES_ADD = ADD << 8;
public static final int TYPE_CLASSES_REMOVE = REMOVE << 8;
public static final int TYPE_CLASSES_CHANGE = CHANGE << 8;
public static final int TYPE_CLASSES = (ADD | REMOVE | CHANGE) << 8;
public static final int TYPE_CONSTRUCTORS_ADD = ADD << 12;
public static final int TYPE_CONSTRUCTORS_REMOVE = REMOVE << 12;
public static final int TYPE_CONSTRUCTORS_CHANGE = CHANGE << 12;
public static final int TYPE_CONSTRUCTORS = (ADD | REMOVE | CHANGE) << 12;
public static final int TYPE_INITIALIZERS_ADD = ADD << 16;
public static final int TYPE_INITIALIZERS_REMOVE = REMOVE << 16;
public static final int TYPE_INITIALIZERS_CHANGE = CHANGE << 16;
public static final int TYPE_INITIALIZERS = (ADD | REMOVE | CHANGE) << 16;
public static final int TYPE_SOURCE_CHECK_SELF = 0x100000;
public static final int TYPE_SOURCE_CHECK_DEEP = 0x200000;
public static final int ADD_MASK = 0x11111;
public static final int REMOVE_MASK = 0x22222;
public static final int CHANGE_MASK = 0x44444;
public static final int TYPE_ALL = 0xFFFFF;
static final JavaSynchronizationSettings SETTINGS =
(JavaSynchronizationSettings)JavaSynchronizationSettings.findObject(JavaSynchronizationSettings.class);
/** The basic type of connection between two sources.
*/
public static class Type implements ConnectionCookie.Type {
/** The filter of events */
int filter;
static final long serialVersionUID =-6323669534600303244L;
/** Creates new type of java connection.
* @param filter The filter - consist of TYPE_XXX constants.
*/
public Type(int filter) {
this.filter = filter;
}
/** The class that is passed into the listener's <CODE>notify</CODE>
* method when an event of this type is fired.
*
* @return ImplementsEvent.class
*/
public Class getEventClass () {
return Event.class;
}
/** Implements connection is persistent.
* @return always <CODE>true</CODE>
*/
public boolean isPersistent () {
return true;
}
/** Test whether this type overlaps with the specified one.
* @return <CODE>true</CODE> if the types are overlapped
*/
public boolean overlaps(ConnectionCookie.Type type) {
if (type instanceof Type)
return (filter & ((Type)type).filter) != 0;
return false;
}
/**
* @return the filter of this type which was passed in the constructor
*/
public int getFilter() {
return filter;
}
public boolean equals(Object o) {
return (o instanceof Type) && (((Type)o).filter == filter);
}
public int hashCode() {
return filter;
}
};
/** The basic type of connection between two sources
* - interface and implementation.
*/
public static final Type IMPLEMENTS = new Type(TYPE_METHODS_ADD | TYPE_METHODS_CHANGE);
//================================================
public static class Change {
int changeType;
Element oldElement;
Element newElement;
Element[] elements;
Change(int changeType) {
this(changeType, null, null, null);
}
Change(int changeType, Element[] elements) {
this(changeType, null, null, elements);
}
Change(int changeType, Element oldElement, Element newElement) {
this(changeType, oldElement, newElement, null);
}
private Change(int changeType, Element oldElement, Element newElement, Element[] elements) {
this.changeType = changeType;
this.oldElement = oldElement;
this.newElement = newElement;
this.elements = elements;
}
public int getChangeType() {
return changeType;
}
public Element getNewElement() {
return newElement;
}
public Element getOldElement() {
return oldElement;
}
public Element[] getElements() {
return elements;
}
}
public static class Event extends ConnectionCookie.Event {
Change[] changes;
static final long serialVersionUID =-3347417315663192416L;
Event(Node n, Change[] changes, Type type) {
super(n, type);
this.changes = changes;
}
public Change[] getChanges() {
return changes;
}
}
/** This class represents one change during connection
* synchronization between two classes. If user accept it,
* there could be called <CODE>process</CODE> method to
* make the change.
*/
public abstract static class ChangeProcessor {
/** Display name of the change */
private String displayName;
/** Create new change
* @param displayName Display name of the change
*/
public ChangeProcessor(String displayName) {
this.displayName = displayName;
}
/**
* @return Display name of the change
*/
public String getDisplayName() {
return displayName;
}
/** Process the change.
* @exception SourceException if any problem occured with
* modification of the source code.
*/
public abstract void process() throws SourceException;
}
private static Dialog confirmChangesDialog;
private static DialogDescriptor confirmChangesDescriptor;
static ConnectionPanel connectionPanel;
static JButton processButton;
static JButton processAllButton;
/** Opens the dialog with found changes and ask user
* to confirm them.
* @param changes The list of the ChangeProcessor objects
* @param synchType the current synchronization type
* @return new value of synchronizationType after dialog closed.
*/
public static synchronized byte showChangesDialog(List changes, byte synchType) {
if (confirmChangesDescriptor == null) {
processButton = new JButton (Util.getString("LAB_processButton"));
processAllButton = new JButton (Util.getString("LAB_processAllButton"));
final Object [] options = new Object [] {
processButton,
processAllButton
};
final Object [] additionalOptions = new Object [] {
new JButton (Util.getString("LAB_closeButton"))
};
connectionPanel = new ConnectionPanel();
confirmChangesDescriptor = new DialogDescriptor(
connectionPanel,
Util.getString("LAB_ConfirmDialog"),
true,
options,
processButton,
DialogDescriptor.RIGHT_ALIGN,
new HelpCtx (JavaConnections.class.getName () + ".dialog"), // NOI18N
new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getSource() instanceof Component) {
Component root;
// hack to avoid multiple calls for disposed dialogs:
root = SwingUtilities.getRoot((Component)e.getSource());
if (confirmChangesDialog == null || !root.isDisplayable()) {
return;
}
}
if (options[0].equals(e.getSource())) {
int min = connectionPanel.changesList.getMinSelectionIndex();
int max = connectionPanel.changesList.getMaxSelectionIndex();
for (int i = max; i >= min; i--) {
if (connectionPanel.changesList.isSelectedIndex(i)) {
ChangeProcessor p = (ChangeProcessor)connectionPanel.listModel.getElementAt(i);
try {
p.process();
}
catch (SourceException ee) {
TopManager.getDefault().notify(
new NotifyDescriptor.Exception(ee));
//TopManager.getDefault().notifyException(ee);
continue;
}
connectionPanel.listModel.removeElementAt(i);
}
}
if (connectionPanel.listModel.isEmpty()) {
confirmChangesDialog.setVisible(false);
}
}
else if (options[1].equals(e.getSource())) {
Enumeration en = connectionPanel.listModel.elements();
while (en.hasMoreElements()) {
ChangeProcessor processor = (ChangeProcessor) en.nextElement();
try {
processor.process();
}
catch (SourceException ee) {
TopManager.getDefault().notify(
new NotifyDescriptor.Exception(ee));
}
}
confirmChangesDialog.setVisible(false);
connectionPanel.setChanges(null);
}
else if (additionalOptions[0].equals(e.getSource())) {
confirmChangesDialog.setVisible(false);
connectionPanel.setChanges(null);
}
}
}
);
confirmChangesDescriptor.setAdditionalOptions (additionalOptions);
}
processButton.setEnabled(false);
processAllButton.requestFocus();
connectionPanel.setChanges(changes);
connectionPanel.setRadio(synchType);
try {
if (confirmChangesDialog != null) {
throw new IllegalStateException();
}
confirmChangesDialog = TopManager.getDefault().createDialog(confirmChangesDescriptor);
confirmChangesDialog.show();
return connectionPanel.getRadio();
} finally {
confirmChangesDialog.dispose();
confirmChangesDialog = null;
confirmChangesDescriptor = null;
}
}
public static void compareMethods(final ClassElement dest,
ClassElement src, List changeProcessors,
String addMessage, String updateMessage) {
final MethodElement[] oldMethods = dest.getMethods();
final MethodElement[] newMethods = src.getMethods();
int newSize = newMethods.length;
MethodElementImpl[] newMethodsImpl = new MethodElementImpl[newSize];
for (int i = 0; i < newSize; i++) {
MethodElementImpl impl = new MethodElementImpl();
MethodElement m = newMethods[i];
impl.type = m.getReturn();
impl.name = m.getName();
impl.parameters = m.getParameters();
impl.exceptions = m.getExceptions();
impl.mod = m.getModifiers();
newMethodsImpl[i] = impl;
}
int[] result = ElementsCollection.pairElements(oldMethods, newMethodsImpl, ElementsCollection.Method.COMPARATORS);
MessageFormat addMsg = new MessageFormat(addMessage);
MessageFormat updateMsg = new MessageFormat(updateMessage);
for (int i = 0; i < newSize; i++) {
final MethodElement m = newMethods[i];
if (result[i] == -1) {
changeProcessors.add(new ChangeProcessor(addMsg.format(new Object[] { m.getName().toString() })) {
public void process() throws SourceException {
dest.addMethod(m);
}
});
}
else {
final MethodElement oldMethod = oldMethods[result[i]];
final MethodElementImpl impl = (MethodElementImpl) oldMethod.getCookie(MethodElementImpl.class);
final boolean[] changes = new boolean[] {
!impl.type.compareTo(m.getReturn(), false),
impl.parameters.length != m.getParameters().length,
impl.exceptions.length != m.getExceptions().length,
impl.mod != m.getModifiers(),
!impl.name.compareTo(m.getName(), false)
};
for (int j = 0; (j < impl.parameters.length) && !changes[1]; j++)
if (!impl.parameters[j].compareTo(m.getParameters()[j], false, false))
changes[1] = true;
for (int j = 0; (j < impl.exceptions.length) && !changes[2]; j++)
if (!impl.exceptions[j].compareTo(m.getExceptions()[j], false))
changes[2] = true;
boolean ch = false;
for (int j = 0; (j < changes.length) && !ch; j++)
ch |= changes[j];
if (ch) {
changeProcessors.add(new ChangeProcessor(updateMsg.format(new Object[] { impl.name.toString() })) {
public void process() throws SourceException {
if (changes[0])
oldMethod.setReturn(m.getReturn());
if (changes[1])
oldMethod.setParameters(m.getParameters());
if (changes[2])
oldMethod.setExceptions(m.getExceptions());
if (changes[3])
oldMethod.setModifiers(m.getModifiers());
if (changes[4])
oldMethod.setName(m.getName());
}
});
}
}
}
}
static class ConnectionPanel extends JPanel {
private JPanel changesPanel;
private JScrollPane jScrollPane1;
private JList changesList;
private JPanel bottomPanel;
private JPanel radioPanel;
private JRadioButton radioDisable;
private JRadioButton radioConfirm;
private JRadioButton radioAuto;
DefaultListModel listModel;
static final long serialVersionUID =-799308237208355590L;
/** Initializes the Form */
public ConnectionPanel() {
setLayout (new java.awt.BorderLayout ());
setBorder(new EmptyBorder(5, 5, 5, 5));
changesPanel = new JPanel ();
changesPanel.setLayout (new java.awt.BorderLayout (5, 5));
changesPanel.setBorder(new TitledBorder(Util.getString("LAB_ChangesList")));
jScrollPane1 = new JScrollPane ();
listModel = new DefaultListModel();
changesList = new JList (listModel);
changesList.setToolTipText (Util.getString("HINT_ChangesList"));
changesList.setCellRenderer(new ChangesListCellRenderer());
changesList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
processButton.setEnabled(!changesList.isSelectionEmpty());
}
});
jScrollPane1.setViewportView (changesList);
changesPanel.add (jScrollPane1, "Center"); // NOI18N
add (changesPanel, "Center"); // NOI18N
radioPanel = new JPanel ();
radioPanel.setLayout (new java.awt.GridLayout (3, 1));
radioPanel.setBorder(new CompoundBorder(
new TitledBorder(Util.getString("LAB_SynchMode")),
new EmptyBorder(5, 5, 5, 5))
);
radioDisable = new JRadioButton ();
radioDisable.setText (Util.getString("LAB_radioDisable"));
radioPanel.add (radioDisable);
radioConfirm = new JRadioButton ();
radioConfirm.setText (Util.getString("LAB_radioConfirm"));
radioPanel.add (radioConfirm);
radioAuto = new JRadioButton ();
radioAuto.setText (Util.getString("LAB_radioAuto"));
radioPanel.add (radioAuto);
ButtonGroup group = new ButtonGroup();
group.add(radioDisable);
group.add(radioConfirm);
group.add(radioAuto);
add (radioPanel, "South"); // NOI18N
}
public java.awt.Dimension getPreferredSize() {
java.awt.Dimension d = super.getPreferredSize();
if (d.height < 300)
d.height = 300;
return d;
}
byte getRadio() {
if (radioDisable.isSelected())
return JavaDataObject.CONNECT_NOT;
else if (radioConfirm.isSelected())
return JavaDataObject.CONNECT_CONFIRM;
else
return JavaDataObject.CONNECT_AUTO;
}
void setRadio(byte button) {
switch (button) {
case JavaDataObject.CONNECT_NOT:
radioDisable.setSelected(true);
break;
case JavaDataObject.CONNECT_CONFIRM:
radioConfirm.setSelected(true);
break;
case JavaDataObject.CONNECT_AUTO:
radioAuto.setSelected(true);
break;
}
}
synchronized void setChanges(List changes) {
listModel.clear();
if (changes != null) {
Iterator it = changes.iterator();
while (it.hasNext())
listModel.addElement(it.next());
}
}
}
static class ChangesListCellRenderer extends DefaultListCellRenderer {
static final long serialVersionUID =-8439520404877315783L;
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected,
boolean cellHasFocus) {
Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if ((comp instanceof JLabel) && (value instanceof ChangeProcessor)) {
((JLabel)comp).setText(((ChangeProcessor)value).getDisplayName());
}
return comp;
}
}
}
/*
* Log
* 17 Gandalf-post-FCS1.13.1.2 4/17/00 Svatopluk Dedic Fixed ClassCastException
* when pressing ESC
* 16 Gandalf-post-FCS1.13.1.1 4/3/00 Svatopluk Dedic Fixed NPE
* 15 Gandalf-post-FCS1.13.1.0 3/8/00 Svatopluk Dedic SourceException risen
* during sync processing displays only info box
* 14 Gandalf 1.13 3/8/00 Svatopluk Dedic Static reference to
* Confirmation dialog is being cleared after it closes.
* 13 Gandalf 1.12 1/12/00 Petr Hamernik i18n: perl script used (
* //NOI18N comments added )
* 12 Gandalf 1.11 11/27/99 Patrik Knakal
* 11 Gandalf 1.10 10/27/99 Petr Hamernik another bug in comparing
* methods
* 10 Gandalf 1.9 10/27/99 Petr Hamernik fixed bug in comparing
* methods
* 9 Gandalf 1.8 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems copyright in file comment
* 8 Gandalf 1.7 9/15/99 Petr Hamernik compareMethods
* implemented
* 7 Gandalf 1.6 9/10/99 Petr Hamernik some comments
* 6 Gandalf 1.5 8/18/99 Petr Hamernik i18n
* 5 Gandalf 1.4 8/9/99 Ian Formanek Generated Serial Version
* UID
* 4 Gandalf 1.3 7/23/99 Petr Hamernik java connection changes
* 3 Gandalf 1.2 7/8/99 Jesse Glick Context help.
* 2 Gandalf 1.1 6/9/99 Ian Formanek ---- Package Change To
* org.openide ----
* 1 Gandalf 1.0 6/2/99 Petr Hamernik
* $
*/